package protocol

import (
	"cybertwin/utils/configtools"
	"cybertwin/utils/cryptotools"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/panjf2000/gnet/v2"
	log "github.com/sirupsen/logrus"
	"math"
)

// Type 定义枚举类型
type Type uint16

type AccessProtocolCodec struct{}

type Codec struct {
	AccessProtocolCodec   AccessProtocolCodec
	RequestProtocolCodec  RequestProtocolCodec
	ResponseProtocolCodec ResponseProtocolCodec
}

var CodecUtils Codec

// type类型
const (
	TYPE_UNKNOWN               Type = 1
	ACCESS                     Type = 2 // 没使用的变量
	TYPE_REGISTER              Type = 3
	TYPE_USER_REGISTER         Type = 4
	TYPE_OFFLINE               Type = 5
	TYPE_RESPONSE              Type = 6
	TYPE_USER_RESPONSE         Type = 7
	TYPE_ACCESSHELLO           Type = 8  // access hello类型
	TYPE_ACCESSHELLO_RESP      Type = 9  // access hello类型返回值，body中应该包含证书与接入点的pk
	TYPE_HID_CHECK             Type = 10 // ue向ap发送hid+随机数r1
	TYPE_HID_CHECK_RESP        Type = 11 // ap向设备ue发送ctid以及随机数r2
	TYPE_TOKEN_APPLY           Type = 12 // ue向ap申请token
	TYPE_TOKEN_APPLY_RESP      Type = 13 // ap向ue返回token值与r3
	TYPE_ACCESS_CYBERTWIN      Type = 14 // ue向cybertwin发起带有token的请求，欲进行ck3的协商
	TYPE_ACCESS_CYBERTWIN_RESP Type = 15 // cybertwin返回随机数r4
	TYPE_CK3_REQUEST           Type = 16 // ue和cybertwin将会使用ck3进行通信
	TYPE_CK3_RESP              Type = 17 // 如上（14的response）
	TYPE_AP_INFORM_CYBERTWIN   Type = 18 // ap告知cybertwin有新的设备接入，并告知r3，用于生成ck3
	TYPE_UE_CYBERTWIN_REQUEST  Type = 19 // ue向cybertwin发送真正的云服务请求
	TYPE_CYBERTWIN_UE_RESPONSE Type = 20 // cybertwin向ue返回请求的响应
)

const (
	magicNumber           uint16 = 0x16
	magicNumberSize              = 2 // magicNumberSize magicNumber的大小
	dataSizeSize                 = 4 // 存放data数据大小那个字段需要用8bytes
	versionSize                  = 2
	typeSize                     = 2
	twoBytesPlaceholder          = 2
	fourBytesPlaceholder         = 4
	eightBytesPlaceholder        = 8
	encryptSize                  = 2
	version               uint16 = 0x1
)

var ErrIncompleteBuf = errors.New("获取到不完整的包")
var DataOutOfBound = errors.New("data长度超出uint32最大长度，需要分段")

/*
* 0           2              4                            8
* +--------------------------+----------------------------+
* |magicNumber|   version    |         dataSize           |
* +--------------------------+----------------------------+
* |  type     |  is_encrypt  |        placeholder         |
* +--------------------------+----------------------------+
* |                      placeholder                      |
* +--------------------------+----------------------------+
* +                      body bytes                       +
* |                       ... ...                         |
* +--------------------------+----------------------------+
 */

func (value Type) GetName() string {
	switch value {
	case TYPE_OFFLINE:
		return "TYPE_OFFLINE"
	case TYPE_REGISTER:
		return "TYPE_REGISTER"
	case TYPE_USER_REGISTER:
		return "TYPE_USER_REGISTER"
	case TYPE_USER_RESPONSE:
		return "TYPE_USER_REGISTER"
	case TYPE_RESPONSE:
		return "TYPE_REGISTER"
	case ACCESS:
		return "ACCESS"
	case TYPE_UNKNOWN:
		return "TYPE_UNKNOWN"
	case TYPE_ACCESSHELLO:
		return "TYPE_ACCESSHELLO"
	case TYPE_ACCESSHELLO_RESP:
		return "TYPE_ACCESSHELLO_RESP"
	case TYPE_HID_CHECK:
		return "TYPE_HID_CHECK"
	case TYPE_HID_CHECK_RESP:
		return "TYPE_HID_CHECK_RESP"
	case TYPE_TOKEN_APPLY:
		return "TYPE_TOKEN_APPLY"
	case TYPE_TOKEN_APPLY_RESP:
		return "TYPE_TOKEN_APPLY_RESP"
	case TYPE_ACCESS_CYBERTWIN:
		return "TYPE_ACCESS_CYBERTWIN"
	case TYPE_ACCESS_CYBERTWIN_RESP:
		return "TYPE_ACESS_CYBERTWIN_RESP"
	case TYPE_CK3_REQUEST:
		return "TYPE_CK3_REQUEST"
	case TYPE_CK3_RESP:
		return "TYPE_CK3_RESP"
	case TYPE_AP_INFORM_CYBERTWIN:
		return "TYPE_AP_INFORM_CYBERTWIN"
	case TYPE_UE_CYBERTWIN_REQUEST:
		return "TYPE_UE_CYBERTWIN_REQUEST"
	case TYPE_CYBERTWIN_UE_RESPONSE:
		return "TYPE_CYBERTWIN_UE_RESPONSE"
	default:
		return ""
	}
}

func GetVersion(req []byte) int16 {
	var first16bits []byte = req[0:2]
	// 前16bits转化为INT类型返回
	return bytesToInt16(first16bits)
}

// WriteVersion 若能保证p数组长度>=4，则可以不接返回值；否则需要接返回值，因为涉及到扩容
// Deprecated
func WriteVersion(p []byte, version int16) []byte {
	if len(p) < 4 {
		p = make([]byte, 4)
	}
	versionInBytes := int16ToBytes(version)
	p[0] = versionInBytes[0]
	p[1] = versionInBytes[1]
	return p
}

// GetTypeName
// Deprecated
func GetTypeName(req []byte) (string, error) {
	if len(req) < 4 {
		log.Println("协议长度有误")
		return "", errors.New("协议长度有误")
	}
	toInt16 := Type(bytesToInt16(req[2:4]))
	return toInt16.GetName(), nil
}

// WriteType
// Deprecated
func WriteType(p []byte, protocolType Type) []byte {
	bytes := int16ToBytes(int16(protocolType))
	p[2] = bytes[0]
	p[3] = bytes[1]
	return p
}

// WriteData
// Deprecated
func WriteData(p []byte, data []byte) ([]byte, error) {
	switch GetVersion(p) {
	case 1:
		return append(p, data...), nil
	}
	return nil, errors.New("版本号尚未支持")
}

func int16ToBytes(i int16) []byte {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint16(buf, uint16(i))
	return buf
}

func bytesToInt16(buf []byte) int16 {
	return int16(binary.BigEndian.Uint16(buf))
}

func (codec *AccessProtocolCodec) Encode(data []byte, pType Type) ([]byte, error) {
	// dataOffset的值如前面协议所示
	dataOffset := magicNumberSize + versionSize + dataSizeSize +
		typeSize + encryptSize + fourBytesPlaceholder + eightBytesPlaceholder
	head, err := appendHeader(data, pType) // header长度为24bytes，其中预留了14bytes的空间备用
	if err != nil {
		return nil, err
	}
	dataSize := len(data)
	res := make([]byte, dataOffset+dataSize)
	copy(res[:dataOffset], head)
	copy(res[dataOffset:], data)
	return res, nil
}

func (codec *AccessProtocolCodec) EncryptEncode(data []byte, pType Type) ([]byte, error) {
	// dataOffset的值如前面协议所示
	dataOffset := magicNumberSize + versionSize + dataSizeSize +
		typeSize + encryptSize + fourBytesPlaceholder + eightBytesPlaceholder

	dir, filename := configtools.GetRegisterPublicKeyFile()
	pk := cryptotools.RSA_LoadPublicKey(dir, filename)
	//log.Infoln("[EncryptEncode]-使用" + dir + "/" + filename + "公钥进行加密")
	data, err := cryptotools.RSA_Encrypt(data, pk)
	if err != nil {
		log.Errorln("[EncryptEncode]-使用公钥加密失败")
		return nil, err
	}
	// 加载公钥进行加密
	head, err := appendHeaderWithEncryption(data, pType) // header长度为24bytes，其中预留了14bytes的空间备用
	if err != nil {
		return nil, err
	}
	dataSize := len(data)
	//log.Infoln("[EncryptEncode]-dataSize=" + fmt.Sprintf("%d", dataSize))
	res := make([]byte, dataOffset+dataSize)
	copy(res[:dataOffset], head)
	copy(res[dataOffset:], data)
	return res, nil
}

func (codec *AccessProtocolCodec) AESEncode(data []byte, pType Type, key []byte) ([]byte, error) {
	// dataOffset的值如前面协议所示
	dataOffset := magicNumberSize + versionSize + dataSizeSize +
		typeSize + encryptSize + fourBytesPlaceholder + eightBytesPlaceholder

	data, err := cryptotools.AES_Encrypt(data, key)
	if err != nil {
		log.Errorln("[EncryptEncode]-使用对称密钥加密失败:", err.Error())
		return nil, err
	}
	// 加载公钥进行加密
	head, err := appendHeaderWithEncryption(data, pType) // header长度为24bytes，其中预留了14bytes的空间备用
	if err != nil {
		return nil, err
	}
	dataSize := len(data)
	//log.Infoln("[EncryptEncode]-dataSize=" + fmt.Sprintf("%d", dataSize))
	res := make([]byte, dataOffset+dataSize)
	copy(res[:dataOffset], head)
	copy(res[dataOffset:], data)
	return res, nil
}

// Decode 接受一个连接，返回协议解析后的body内容，协议类型以及body内容是否加密
func (codec *AccessProtocolCodec) Decode(c gnet.Conn) ([]byte, string, bool, error) {
	//log.Infoln("进行decode操作")
	dataOffset := magicNumberSize + versionSize + dataSizeSize +
		typeSize + encryptSize + fourBytesPlaceholder + eightBytesPlaceholder
	buf, _ := c.Peek(dataOffset)
	if len(buf) < dataOffset {
		return nil, "", false, ErrIncompleteBuf
	}
	// 校验magicNumber
	curMagicNumber := binary.BigEndian.Uint16(buf[:magicNumberSize])
	if magicNumber != curMagicNumber {
		return nil, "", false, errors.New("invalid Magic Number")
	}
	dataSizeOffset := magicNumberSize + versionSize
	curVersion := binary.BigEndian.Uint16(buf[magicNumberSize:dataSizeOffset])
	if curVersion == 0x1 {
		// 获取dataSize的长度
		typeOffset := dataSizeOffset + dataSizeSize
		dataSize := int(binary.BigEndian.Uint32(buf[dataSizeOffset:typeOffset]))
		//log.Infoln("[Decode]-dataSize=" + fmt.Sprintf("%d", dataSize))
		encryptOffset := typeOffset + 2
		curType := Type(binary.BigEndian.Uint16(buf[typeOffset:encryptOffset]))
		encrypt := int(binary.BigEndian.Uint16(buf[encryptOffset : encryptOffset+encryptSize]))
		//log.Infoln("[Decode]-encrypt = " + fmt.Sprintf("%d", encrypt))
		curTypeName := curType.GetName()
		if curType.GetName() == "" || curType.GetName() == "TYPE_UNKNOWN" {
			return nil, "", false, errors.New("invalid type:" + curTypeName)
		}
		msgLen := dataOffset + dataSize
		//log.Infoln("[Decode]-msgLen = " + fmt.Sprintf("%d", msgLen))
		if c.InboundBuffered() < msgLen {
			return nil, "", false, ErrIncompleteBuf
		}

		buf, _ = c.Peek(msgLen)
		_, _ = c.Discard(msgLen)

		return buf[dataOffset:msgLen], curTypeName, encryptConvertor(encrypt), nil
	}

	return nil, "", false, errors.New("no matched version")

}

func appendHeader(data []byte, pType Type) ([]byte, error) {
	res := make([]byte, 0)
	res = binary.BigEndian.AppendUint16(res, magicNumber)
	res = binary.BigEndian.AppendUint16(res, version)
	var dataSize uint32
	if dataSize = uint32(len(data)); dataSize > (math.MaxUint32) {
		return nil, DataOutOfBound
	}
	res = binary.BigEndian.AppendUint32(res, dataSize)
	res = binary.BigEndian.AppendUint16(res, uint16(pType))

	// placeholder填充
	res = binary.BigEndian.AppendUint16(res, 0x0)
	res = binary.BigEndian.AppendUint32(res, 0x0)
	res = binary.BigEndian.AppendUint64(res, 0x0)

	return res, nil
}

func appendHeaderWithEncryption(data []byte, pType Type) ([]byte, error) {
	res := make([]byte, 0)
	res = binary.BigEndian.AppendUint16(res, magicNumber)
	res = binary.BigEndian.AppendUint16(res, version)
	var dataSize uint32
	if dataSize = uint32(len(data)); dataSize > (math.MaxUint32) {
		return nil, DataOutOfBound
	}
	res = binary.BigEndian.AppendUint32(res, dataSize)
	res = binary.BigEndian.AppendUint16(res, uint16(pType))
	res = binary.BigEndian.AppendUint16(res, uint16(1))

	// placeholder填充
	res = binary.BigEndian.AppendUint32(res, 0x0)
	res = binary.BigEndian.AppendUint64(res, 0x0)

	return res, nil
}

// encryptConvertor当val为0时，返回false；否则返回true——为了与原版本兼容
func encryptConvertor(val int) bool {
	if val == 0 {
		return false
	}
	return true
}

func (codec *AccessProtocolCodec) DecodeFromBytes(buf []byte) (data []byte, typeName string, isEncrypt bool, err error) {
	dataOffset := magicNumberSize + versionSize + dataSizeSize +
		typeSize + encryptSize + fourBytesPlaceholder + eightBytesPlaceholder
	data = nil
	typeName = ""
	isEncrypt = false
	err = nil

	if len(buf) < dataOffset {
		err = ErrIncompleteBuf
		return
	}

	curMagicNumber := binary.BigEndian.Uint16(buf[:magicNumberSize])
	if magicNumber != curMagicNumber {
		err = errors.New("invalid Magic Number")
		return
	}
	dataSizeOffset := magicNumberSize + versionSize
	curVersion := binary.BigEndian.Uint16(buf[magicNumberSize:dataSizeOffset])
	if curVersion == 0x1 {
		// 获取dataSize的长度
		typeOffset := dataSizeOffset + dataSizeSize
		dataSize := int(binary.BigEndian.Uint32(buf[dataSizeOffset:typeOffset]))
		//log.Infoln("[Decode]-dataSize=" + fmt.Sprintf("%d", dataSize))
		encryptOffset := typeOffset + encryptSize
		curType := Type(binary.BigEndian.Uint16(buf[typeOffset:encryptOffset]))
		encrypt := int(binary.BigEndian.Uint16(buf[encryptOffset : encryptOffset+encryptSize]))
		isEncrypt = encryptConvertor(encrypt) // 返回值isEncrypt填充
		//log.Infoln("[Decode]-encrypt = " + fmt.Sprintf("%d", encrypt))
		typeName = curType.GetName() // 返回值typeName填充
		if curType.GetName() == "" || curType.GetName() == "TYPE_UNKNOWN" {
			err = fmt.Errorf("invalid type: %s, type: %d", typeName, curType)
			return
		}
		msgLen := dataOffset + dataSize
		if msgLen != len(buf) {
			log.Errorf("msgLen = %d, real buf = %d", msgLen, len(buf))
			err = ErrIncompleteBuf
			return
		}
		data = buf[dataOffset:] // 返回值data填充
		//log.Infoln("[Decode]-msgLen = " + fmt.Sprintf("%d", msgLen))
		return
	}
	err = errors.New("no matched version")
	return
}
